﻿using Marmat.Forms;
using Marmat.FrameWork;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Marmat.Template.WinForm
{
    /// <summary>
    /// 高级文件视图
    /// </summary>
    [ToolboxItem(true)]
    public partial class AdvFileView : Marmat.Forms.FileManager
    {
        private readonly Pen pen = new Pen(Color.FromArgb(238, 238, 238));
        private FileItem mouse_fileItem;
        private Rectangle additionRectangle;
        private List<FileItem> files = new List<FileItem>();
        private readonly Dictionary<FileItem, Rectangle> rectangleCollection = new Dictionary<FileItem, Rectangle>();
        private readonly Dictionary<Marmat.Foundation.IButtonItem, Rectangle> buttonRectangleCollection = new Dictionary<Foundation.IButtonItem, Rectangle>();

        /// <summary>
        /// 获取或设置一个值，该值指示上传文件时对话框是否允许选择多个文件。。
        /// </summary>
        [DefaultValue(true)]
        public bool Multiselect { get; set; } = true;

        /// <summary>
        /// 设置筛选器
        /// </summary>
        [DefaultValue("所有文件|*.*")]
        public string Filter { get; set; } = "所有文件|*.*";

        [DefaultValue(typeof(Color), "White")]
        public override Color BackColor { get => base.BackColor; set => base.BackColor = value; }

        [DefaultValue(typeof(Font), "微软雅黑, 9pt")]
        public override Font Font { get => base.Font; set => base.Font = value; }

        protected virtual string BuildFileID(string fileName)
        {
            System.IO.FileInfo info = new System.IO.FileInfo(fileName);
            string id = System.Guid.NewGuid().ToString().Replace("-", null).ToLower() + info.Extension;
            if (!string.IsNullOrEmpty(this.RootDir))
                id = System.IO.Path.Combine(this.RootDir, id);
            return id;
        }


        public AdvFileView()
        {
            this.CustomSaveFileing += this.AdvFileView_CustomSaveFileing;
            this.DownloadItemClick += this.AdvFileView_DownloadItemClick;
            this.BeforeAddFile += this.AdvFileView_BeforeAddFile;
            this.BackColor = Color.White;
            this.Font = new Font("微软雅黑", 9);
        }

        private void AdvFileView_BeforeAddFile(object sender, FileItemCancelEventArgs e)
        {
            var userProvider = SystemCommand.GetUserProvider();
            e.Item.Description = $"来自 {userProvider.NickName ?? userProvider.UserID}  |  {DateTime.Now:yyyy年MM月dd日 HH:mm}";
        }

        private void AdvFileView_DownloadItemClick(object sender, FileItemEventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            FileInfo info = new FileInfo(e.Item.Name);
            dialog.Filter = $"文件|*{info.Extension}";
            dialog.FileName = e.Item.Name;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                var fileService = SystemCommand.GetService<Marmat.Foundation.IFileManager>();
                if (fileService is null)
                    throw new NullReferenceException($"没有找到 {typeof(Marmat.Foundation.IFileManager).FullName} 服务。");
                fileService.DownloadAsync(e.Item.Id, dialog.FileName);
            }
        }

        private void AdvFileView_CustomSaveFileing(object sender, FileManagerCustomSaveingEventArgs e)
        {
            var fileService = SystemCommand.GetService<Marmat.Foundation.IFileManager>();
            if (fileService is null)
                throw new NullReferenceException($"没有找到 {typeof(Marmat.Foundation.IFileManager).FullName} 服务。");
            foreach (var file in e.Files)
            {
                if (file.Status == FileManagerItemStatusEnum.Add)
                {
                    fileService.Upload(file.Item.LocalPath, file.Item.Id);
                    file.Succeed = true;
                }
                else if (file.Status == FileManagerItemStatusEnum.Delete)
                {
                    fileService.Delete(file.Item.Id);
                    file.Succeed = true;
                }
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            int top = this.Padding.Top - this.VerticalScroll.Value;
            int left = this.Padding.Left;
            int width = this.ClientSize.Width - this.Padding.Right - this.Padding.Left;
            Rectangle bounds = new Rectangle(left, top, width, 60);
            rectangleCollection.Clear();
            buttonRectangleCollection.Clear();
            // 绘制头部信息
            TextRenderer.DrawText(e.Graphics, $"共 {this.Files.Count} 个附件", this.Font, bounds, Color.DarkGray, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
            top += bounds.Height;

            // 绘制上传标签
            additionRectangle = default;
            if (!this.IsReadOnly)
            {
                string additionString = "+ 添加附件";
                var additionSize = TextRenderer.MeasureText(additionString, this.Font);
                additionRectangle = new Rectangle(bounds.Right - additionSize.Width, bounds.Top + (bounds.Height - additionSize.Height) / 2, additionSize.Width, additionSize.Height);
                TextRenderer.DrawText(e.Graphics, additionString, this.Font, additionRectangle, Color.DarkGray, TextFormatFlags.Right | TextFormatFlags.VerticalCenter);
            }

            // 绘制列表项
            foreach (var file in this.Files)
            {
                bounds = new Rectangle(left, top, width, 80);
                rectangleCollection[file] = bounds;
                if (bounds.Bottom > 0 && bounds.Top < this.Height)
                    this.drawItem(new PaintEventArgs(e.Graphics, bounds), file);
                top += bounds.Height;
            }
            int height = top + this.Padding.Bottom + this.VerticalScroll.Value;
            this.AutoScrollMinSize = new Size(0, height);
        }

        private void drawItem(PaintEventArgs e, FileItem item)
        {
            Font font = new Font(this.Font.FontFamily, 10.5f);
            Font description_font = new Font(this.Font.FontFamily, 9.0f);
            Rectangle bounds = new Rectangle(e.ClipRectangle.Left, e.ClipRectangle.Top + (e.ClipRectangle.Height - 50) / 2, 100, 50);

            var buttonCollection = this.GetButtonCollection().ToArray();
            int buttonWidth = (buttonCollection.Length * 2 - 1) * 16;
            // 绘制文件图标。
            var icon = this.GetFileBackground(item);
            var icon_bounds = new Rectangle(bounds.X + (bounds.Width - icon.Width) / 2, bounds.Top + (bounds.Height - icon.Height) / 2, icon.Width, icon.Height);
            e.Graphics.DrawImage(icon, icon_bounds);

            // 绘制图片区域边框
            e.Graphics.DrawRectangle(pen, bounds);

            // 绘制文件名称
            bounds = new Rectangle(bounds.Right + 15, bounds.Top, e.ClipRectangle.Right - bounds.Right - buttonWidth - 30, font.Height + 6);
            TextRenderer.DrawText(e.Graphics, item.Name, font, bounds, this.ForeColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis);

            // 绘制文件大小的描述字符串及文件的说明。
            string sizeString = "未知大小";
            if (item.Size < 1024)
                sizeString = string.Format("{0:f}", item.Size.ToString()) + " 字节";
            else if (item.Size < Math.Pow(1024, 2))
                sizeString = string.Format("{0:f}", item.Size / Math.Pow(1024, 1)) + " KB";
            else if (item.Size < Math.Pow(1024, 3))
                sizeString = string.Format("{0:f}", item.Size / Math.Pow(1024, 2)) + " MB";
            else if (item.Size < Math.Pow(1024, 4))
                sizeString = string.Format("{0:f}", item.Size / Math.Pow(1024, 3)) + " GB";
            else if (item.Size < Math.Pow(1024, 5))
                sizeString = string.Format("{0:f}", item.Size / Math.Pow(1024, 4)) + " TB";
            else if (item.Size < Math.Pow(1024, 6))
                sizeString = string.Format("{0:f}", item.Size / Math.Pow(1024, 5)) + " PB";
            else if (item.Size < Math.Pow(1024, 7))
                sizeString = string.Format("{0:f}", item.Size / Math.Pow(1024, 6)) + " EB";
            else if (item.Size < Math.Pow(1024, 8))
                sizeString = string.Format("{0:f}", item.Size / Math.Pow(1024, 7)) + " ZB";
            else if (item.Size < Math.Pow(1024, 9))
                sizeString = string.Format("{0:f}", item.Size / Math.Pow(1024, 8)) + " YB";

            bounds = new Rectangle(bounds.Left, bounds.Bottom, bounds.Width, description_font.Height + 8);
            TextRenderer.DrawText(e.Graphics, $"{sizeString}  {item.Description}", description_font, bounds, Color.Gray, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);

            // 当鼠标移入时绘制操作区域
            if (this.mouse_fileItem == item)
            {
                int buttonRight = 0;
                bounds = new Rectangle(e.ClipRectangle.Right - buttonWidth, e.ClipRectangle.Top + (e.ClipRectangle.Height - 16) / 2, buttonWidth, 16);

                for (int i = 0; i < buttonCollection.Length; i++)
                {
                    var button = buttonCollection[i];
                    var buttonRectangle = new Rectangle(bounds.Left + buttonRight, bounds.Top, button.Icon.Width, button.Icon.Height);
                    buttonRectangleCollection[button] = buttonRectangle;
                    e.Graphics.DrawImage(button.Icon, buttonRectangle);
                    buttonRight += 16 * 2;
                }
            }

            // 绘制底部线条
            e.Graphics.DrawLine(pen, e.ClipRectangle.Left, e.ClipRectangle.Bottom, e.ClipRectangle.Right, e.ClipRectangle.Bottom);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            var mouse_item = this.rectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (this.mouse_fileItem != mouse_item)
            {
                this.mouse_fileItem = mouse_item;
                this.Invalidate();
            }

            bool selected = false;
            selected = additionRectangle.Contains(e.Location);
            if (!selected)
                selected = this.buttonRectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key is not null;
            this.Cursor = selected ? Cursors.Hand : Cursors.Default;
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (e.Button == MouseButtons.Left)
            {
                if (this.additionRectangle.Contains(e.Location))
                {
                    OpenFileDialog dialog = new OpenFileDialog();
                    dialog.Multiselect = this.Multiselect;
                    dialog.Filter = this.Filter;
                    if (dialog.ShowDialog() == DialogResult.OK)
                    {
                        foreach (var fileName in dialog.FileNames)
                        {
                            this.Files.Add(fileName);
                        }
                    }
                }
                else
                {
                    var button = this.buttonRectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
                    if (button is not null)
                        this.OnItemClick(button, this.rectangleCollection.First(x => x.Value.Contains(e.Location)).Key);
                }
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            if (this.mouse_fileItem is not null)
            {
                this.mouse_fileItem = null;
                this.Invalidate();
            }
        }

        private IEnumerable<Marmat.Foundation.ButtonItem> GetButtonCollection()
        {
            yield return new Foundation.ButtonItem("下载", "", Properties.Resources._1);
            if (!this.IsReadOnly)
            {
                yield return new Foundation.ButtonItem("重命名", "", Properties.Resources._2);
                yield return new Foundation.ButtonItem("删除", "", Properties.Resources._3);
            }
        }

        public virtual void OnItemClick(Marmat.Foundation.IButtonItem button, FileItem item)
        {
            switch (button.Name)
            {
                case "下载":
                    FileItemEventArgs args = new FileItemEventArgs(item);
                    this.DownloadItemClick?.Invoke(this, args);
                    break;
                case "重命名":
                    System.IO.FileInfo info = new FileInfo(item.Name);
                    string fileName = System.IO.Path.GetFileNameWithoutExtension(info.Name);
                    if (InputValue.Show(ref fileName, "重命名", "请输入文件的名称："))
                    {
                        if (string.IsNullOrEmpty(fileName.Trim()))
                        {
                            Marmat.Forms.MessageBox.Show("文件名不能为空！", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                            return;
                        }
                        item.Name = fileName + info.Extension;
                        this.SetValue();
                        if (this.BindingField.IsBinding)
                            this.BindingField.WriteValue();
                    }
                    break;
                case "删除":
                    if (Marmat.Forms.MessageBox.Show("是否要删除选中的文件？", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
                        this.Files.Remove(item);
                    break;
            }
        }

        protected Image GetFileBackground(FileItem item)
        {
            return Marmat.Forms.FileIconAPI.GetIcon(item.Name).ToBitmap();
        }

        protected override void NotifyPropertyChanged()
        {
            if (this.ReadOnly == BooleanEnum.Default)
            {
                if (this.BindingField.DataSource is not null && !string.IsNullOrEmpty(this.BindingField.DataMember))
                    this.IsReadOnly = !this.BindingField.DataSource.AllowEdit || this.BindingField.DataSource.GetColumnCollection().FirstOrDefault(item => string.Equals(item.Name, this.BindingField.DataMember)).ReadOnly;
                else
                    this.IsReadOnly = false;
                this.Invalidate();
            }
        }
    }
}
